// Xampler, a MIDI-driven sampler
// Copyright (C) 2006 Nick Thomas
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
// 02110-1301 USA

#ifndef XAMPLER_ENGINE_HPP
#define XAMPLER_ENGINE_HPP

// Holds the state and associcated code of the synthesis thread, which
// talks to JACK and the ALSA sequencer. A single one of these objects
// is instantiated in the main() function.

#include <jack/jack.h>
#include <alsa/asoundlib.h>
#include <glibmm/ustring.h>

namespace xampler
{
    extern int g_sample_rate;

    class Engine
    {
    public:
        // Does very little: just sets up some internal data
        // structures.
        Engine();

        // Readies the engine for operation, by initializing the JACK
        // and ALSA libraries, and starting the synthesis
        // thread. Returns true on success, and false on failure,
        // storing the error message in the given string.
        bool init(Glib::ustring &error);

        // Stops the synthesis thread and shuts down the JACK and ALSA
        // libraries.
        ~Engine();

    private:
        // init() helper routines.
        bool init_jack(Glib::ustring &error);
        bool init_seq(Glib::ustring &error);

        // Destructor helper routines.
        void stop_jack();
        void stop_seq();

        // A wrapper that calls ((Engine)arg)->jack_process(nframes).
        static int jack_processing_callback(jack_nframes_t nframes,
                                            void *arg);

        // Synthesis thread callback and helper functions.
        int jack_process(jack_nframes_t nframes);
        void seq_process();
        int event_time_offset(snd_seq_event_t *event);
        void audio_process(float *out_1, float *out_2,
                           jack_nframes_t nframes);

        // Voice management routines.
        void allocate_voice(int instrument, int sample, int position,
                            float velocity);
        void free_voice(int voice);

        // JACK-related state.
        bool m_jack_initialized;
        jack_client_t *m_jack_client;
        jack_port_t *m_jack_port_out_1;
        jack_port_t *m_jack_port_out_2;

        // ALSAseq-related state.
        bool m_seq_initialized;
        snd_seq_t *m_seq;
        int m_seq_id;
        int m_seq_port;

        union Voice
        {
            struct {
                // The index of the next free voice, or -1 if there
                // are no free voices after this one.
                bool is_free;
                int next_free_voice;
            } free;

            struct {
                bool is_free;
                int instrument;
                int sample;
                float velocity;
                // The position in the sample. This can be negative to
                // indicate a delay before beginning playback.
                int position;
            } used;

            bool is_free;
        };

        const static int MAX_POLYPHONY = 32;

        // Voice-related state.
        int m_first_free_voice;
        Voice m_voices[MAX_POLYPHONY];
    };
}

#endif
